#![feature(exit_status_error)]

use std::{
    collections::HashMap,
    env, fs,
    io::Write,
    path::PathBuf,
    process::{exit, Command},
};

fn config_handle(target: &str, out_dir: &str) {
    let status = match target {
        "alldefconfig" | "allnoconfig" | "allyesconfig" | "guiconfig" | "menuconfig"
        | "oldconfig" | "olddefconfig" | "savedefconfig" | "listnewconfig" => {
            Command::new(target).status().unwrap()
        }
        _ => {
            let mut file = PathBuf::new();
            file.push("configs");
            file.push(target);
            if !file.exists() {
                println!("config file {} is not exist", file.to_str().unwrap());
                exit(1);
            }
            Command::new("defconfig")
                .arg(file.to_str().unwrap())
                .status()
                .unwrap()
        }
    };

    status.exit_ok().unwrap();

    if target.eq("savedefconfig") || target.eq("listnewconfig") {
        return;
    }

    let mut out_header = PathBuf::from(out_dir);
    out_header.push("autoconf.h");

    let mut out_deps = PathBuf::from(out_dir);
    out_deps.push("deps");

    Command::new("genconfig")
        .args(["--header-path", out_header.to_str().unwrap()])
        .args(["--sync-deps", out_deps.to_str().unwrap()])
        .status()
        .unwrap()
        .exit_ok()
        .unwrap();
}

fn prase_auto_conf(out_dir: &str) -> HashMap<String, String> {
    let mut auto_conf_path = PathBuf::from(out_dir);
    auto_conf_path.push("deps");
    auto_conf_path.push("auto.conf");
    let auto_conf = fs::read_to_string(auto_conf_path).unwrap();

    let collect: Vec<&str> = auto_conf.split('\n').collect();
    let mut hash_table: HashMap<String, String> = HashMap::new();
    for s in collect {
        let v: Vec<&str> = s.split('=').collect();
        if v.len() != 2 {
            continue;
        }

        hash_table.insert(v[0].to_string(), v[1].to_string());
    }
    hash_table
}

fn feature_handle(out_dir: &str, conf: &HashMap<String, String>) {
    let path = PathBuf::from(out_dir);
    let mut feature_file = fs::File::create(path.join("features.conf")).unwrap();

    for (k, v) in conf {
        if k.starts_with("CONFIG_FEATURE") {
            write!(feature_file, "CARGO_FEATURES += --features").unwrap();
            writeln!(feature_file, " {}", v).unwrap();
        }
    }
}

fn cargo_config(out_dir: &str, conf: &HashMap<String, String>) {
    let out_cargo_config = PathBuf::from(out_dir);
    let mut f = fs::File::create(out_cargo_config.join("tmp_config.toml")).unwrap();

    let v = conf.get("CONFIG_ARCH").unwrap();
    if v.eq("\"arm64\"") {
        writeln!(f, "[build]").unwrap();
        writeln!(f, "target = \"aarch64-unknown-none\"").unwrap();
        if conf.contains_key("CONFIG_TARGET_VIRTARM64") {
            writeln!(f, "[target.aarch64-unknown-none]").unwrap();
            writeln!(f, "runner = \"qemu-system-aarch64 -M virt -cpu cortex-a57 -smp 8 -m 4G -nographic -kernel\"").unwrap();
        }
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let target = &args[1];
    let out = env::var("KCONFIG_OUTPUT").unwrap();

    config_handle(target.as_str(), out.as_str());

    let conf = prase_auto_conf(out.as_str());

    feature_handle(out.as_str(), &conf);

    cargo_config(out.as_str(), &conf);
}
